/* WebServer high level API
 *
 * 
 * 
 *  
 */

//
#include <stdio.h>
#include <string.h>
//
#include "freertos/FreeRTOS.h"
#include "freertos/task.h"
#include "freertos/event_groups.h"
#include "freertos/portmacro.h"
//
#include "esp_system.h"
#include "esp_log.h"
#include "esp_wifi.h"
#include "esp_event.h"  //event loop
#include "esp_mac.h"
#include "esp_vfs.h"
#include "esp_spiffs.h"
#include "esp_http_server.h"
#include "esp_err.h"  //ESP_OK or ESP_FAIL
//
#include "nvs_flash.h"
//
#include "driver/gpio.h"
//
#include "../main.h"

//for SDCard
#include <sys/stat.h>

/************************************************************************
 * DEFINES
 ************************************************************************/
#define TAG_WEB         "Web"

//用于EMBED_FILES, 先以SDCard+FS替代
// typedef struct 
// {
//     char *who; 
//     const unsigned char *fileStart;
//     const unsigned char *fileEnd;
//     const char *fileType;
// } Web_FileType_t; //自定义类型

typedef struct
{
    const char *who;
    const char *filePath;
    const char *fileType;
}Web_FileTyp_FS_t;


/************************************************************************
 * VARIABLES
 ************************************************************************/
// //indext.html
// extern const unsigned char html_index_start[] asm("_binary_index_html_start");
// extern const unsigned char html_index_end[] asm("_binary_index_html_end");
// //styles.css
// extern const unsigned char html_styles_start[] asm("_binary_styles_css_start");
// extern const unsigned char html_styles_end[] asm("_binary_styles_css_end");
// //scriptA.js
// extern const unsigned char html_scriptA_start[] asm("_binary_scriptA_js_start");
// extern const unsigned char html_scriptA_end[] asm("_binary_scriptA_js_end");
// //scriptB.js
// extern const unsigned char html_scriptB_start[] asm("_binary_scriptB_js_start");
// extern const unsigned char html_scriptB_end[] asm("_binary_scriptB_js_end");
// //scriptC.js
// extern const unsigned char html_scriptC_start[] asm("_binary_scriptC_js_start");
// extern const unsigned char html_scriptC_end[] asm("_binary_scriptC_js_end");
// //scriptD.js
// extern const unsigned char html_scriptD_start[] asm("_binary_scriptD_js_start");
// extern const unsigned char html_scriptD_end[] asm("_binary_scriptD_js_end");
//用于EMBED_FILES, 现以SDCard+FS替代
// //记录所有web文件(使用自定义类型 - Web_FileType) 
// Web_FileType_t WebFiles[] = 
// {
//     {"HTML_Index",      html_index_start,      html_index_end,     "text/html"}, //0
//     {"CSS",             html_styles_start,     html_styles_end,    "text/css"}, //1
//     {"JS_A",            html_scriptA_start,    html_scriptA_end,   "application/javascript"}, //2
//     {"JS_B",            html_scriptB_start,    html_scriptB_end,   "application/javascript"}, //3
//     {"JS_C",            html_scriptC_start,    html_scriptC_end,   "application/javascript"}, //4
//     {"JS_D",            html_scriptD_start,    html_scriptD_end,   "application/javascript"}, //5
// };
//记录Web文件信息
Web_FileTyp_FS_t WebFilesFS[] = 
{
    {"HTML_Index",      "/web_files/index.html",     "text/html"}, //0
    {"CSS",             "/web_files/styles.css",     "text/css"}, //1
    {"JS_A",            "/web_files/scriptA.js",     "application/javascript"}, //2
    {"JS_B",            "/web_files/scriptB.js",     "application/javascript"}, //3
    {"JS_C",            "/web_files/scriptC.js",     "application/javascript"}, //4
    {"JS_D",            "/web_files/scriptD.js",     "application/javascript"}, //5
};

int16_t EPD_Type; //Type_0->a; Type_1->b; ...... Type_25->z; Type_26->A;

//ePaper处理函数
Func_SetImgData_t handler_ePaperSetData = NULL;
Func_FullDisplay_t handler_ePaperDisplay = NULL;
Func_ResetImgIndex_t handler_ePaperResetIdx = NULL;
//SDCard处理函数
Func_SDCardTest_t handler_SDCardTest = NULL;


/************************************************************************
 * FUNCTIONS
 ************************************************************************/
static esp_err_t http_uri_get_handler(httpd_req_t *req);
static esp_err_t http_uri_post_handler(httpd_req_t *req);
//
//用于EMBED_FILES, 先以SDCard+FS替代
// void __sendWebFile(httpd_req_t *req, uint8_t type);
static void __sendWebFileFromFS(httpd_req_t *req, uint8_t type);
static esp_err_t __getSomeChar(char *dest, int dest_len, 
                        char *src, int src_len, 
                        int start, int len);
static int16_t __get8bitFromChar(char *src);
static int16_t __get16bitFromChar(char *src);


esp_err_t WebSer_InitSoftAP()
{
    //------------ 启动WiFi SoftAP

    //--------- 1) LwIP/WiFi初始化
    ESP_ERROR_CHECK(esp_netif_init());  //初始化TCP/IP底层协议栈, 创建LwIP核心任务
    ESP_ERROR_CHECK(esp_event_loop_create_default());  //创建一个default esp_event_loop(在s_default_loop)
    esp_netif_create_default_wifi_ap();  //创建网络接口实例绑定access point

    wifi_init_config_t wifi_init_cfg = WIFI_INIT_CONFIG_DEFAULT();
    ESP_ERROR_CHECK(esp_wifi_init(&wifi_init_cfg));

    //--------- 2) WiFi配置
    wifi_config_t wifi_cfg = {

        .ap = {
            .ssid = "ESP32C3 AP",
            .ssid_len = strlen("ESP32C3 AP"),
            .channel = 1, //1 for default
            .password = "123456780",
            .max_connection = 3,
            .authmode = WIFI_AUTH_WPA2_PSK, //WPA2
            .pmf_cfg = { //???TODO 这是做什么的
                .required = false,
            },
        },
    };
    ESP_ERROR_CHECK(esp_wifi_set_mode(WIFI_MODE_AP));
    ESP_ERROR_CHECK(esp_wifi_set_config(WIFI_IF_AP, &wifi_cfg));

    //--------- 3.1) WiFi启动
    ESP_ERROR_CHECK(esp_wifi_start());

    ESP_LOGI(TAG_WEB, "WiFi softap init done -> SSID:%s, passwd:%s, channle:%d", 
                       "ESP32C3 AP", "123456780", 1);


    //------------ HTTPD服务
    httpd_handle_t httpd_hdl = NULL;
    httpd_config_t httpd_cfg = HTTPD_DEFAULT_CONFIG();
    //
    httpd_cfg.uri_match_fn = httpd_uri_match_wildcard;//使用带通配符的URI允许使用相同的handler处理不同的URIs
    //启动HTTPD
    if(httpd_start(&httpd_hdl, &httpd_cfg) != ESP_OK)
    {
        ESP_LOGI(TAG_WEB, "Failed to start Server!!!");

        return ESP_FAIL;
    }
    ESP_LOGI(TAG_WEB, "Starting HTTP Server on port: '%d'", httpd_cfg.server_port);

    //绑定URI handler
    httpd_uri_t httpd_uri_get = {
        .uri        = "/*",
        .method     = HTTP_GET,
        .handler    = http_uri_get_handler,
        // .user_ctx   = , //传递数据
    };
    httpd_register_uri_handler(httpd_hdl, &httpd_uri_get);
    //
    httpd_uri_t httpd_uri_post = {
        .uri        = "/*",
        .method     = HTTP_POST,
        .handler    = http_uri_post_handler,
        .user_ctx   = NULL
    };
    httpd_register_uri_handler(httpd_hdl, &httpd_uri_post);


    //------------ end
    return ESP_OK;
}

//WebServer的GET的响应函数
//
static esp_err_t http_uri_get_handler(httpd_req_t *req)
{
    uint8_t file_type;

    //判断req->uri内容
    if(strcmp(req->uri, "/index.html") == 0)
        file_type = 0;
    else if(strcmp(req->uri, "/styles.css") == 0)
        file_type = 1;
    else if(strcmp(req->uri, "/scriptA.js") == 0)
        file_type = 2;
    else if(strcmp(req->uri, "/scriptB.js") == 0)
        file_type = 3;
    else if(strcmp(req->uri, "/scriptC.js") == 0)
        file_type = 4;
    else if(strcmp(req->uri, "/scriptD.js") == 0)
        file_type = 5;
    else
    {
        ESP_LOGI(TAG_WEB, "!!! err, request a unknow file");
        return ESP_FAIL;
    }
    
    //返回所需文件
    //用于EMBED_FILES, 先以SDCard+FS替代
    // __sendWebFile(req, file_type);
    __sendWebFileFromFS(req, file_type);


    return ESP_OK;
}

//WebServer的POST的响应函数
//
// 接收的cmd有以下:
//    'EPDx_'
//    'LOAD_'
//    'NEXT_' (暂未使用)
//    'SHOW_'
//
static esp_err_t http_uri_post_handler(httpd_req_t *req)
{
    //------------ step1 receive all data
    char *buf = req->uri;
    int buf_len = strlen(buf);
    char data_cmd[5];

    //--------- 从buf中获取cmd
    __getSomeChar(data_cmd, 5, 
                  buf, buf_len, 
                  buf_len-5, 5);
    // ESP_LOGI(TAG_WEB, "get cmd:%s", data_cmd);
    //--------- 判断&执行cmd
    //------ data_cmd='EPDx_'
    if(strstr(data_cmd, "EPD"))
    {

        // if((handler_ePaperSetData == NULL) || (handler_ePaperDisplay == NULL))
        if(handler_ePaperDisplay == NULL)
        {
            ESP_LOGI(TAG_WEB, "!!! handler func not set !!!");
            return ESP_FAIL;
        }

        //EPD重置数组的idx
        handler_ePaperResetIdx();
        //SDCard中创建待写入文件
        if(SDCard_File_Open("/EPD/photo.txt", FILE_MODE_WRITE) == ESP_FAIL)
            ESP_LOGI(TAG_WEB, "create file in SDCard failed !!!");

        //用'a'表示type 0,
        //若 第一次减'a'结果 > 0, 说明在26范围内
        //若 第一次减'a'结果 < 0, 说明超过了26, 此时需要 减'A' 并为结果追加26
        EPD_Type = data_cmd[3] - (uint16_t)'a'; //'a'=97
        EPD_Type = (EPD_Type < 0) ? (26 + data_cmd[3] - (uint16_t)'A') : EPD_Type; //'A'=65

        ESP_LOGI(TAG_WEB, "get EPD Type:%d", EPD_Type);
    }
    //------ data_cmd='LOAD_'
    else if(strstr(data_cmd, "LOAD")) 
    {
        char data_len[4]; //此时的cmd为固定格式'xxxxLOAD_', xxxx是四个表示数据长度的字符
        __getSomeChar(data_len, 4, 
                      buf, buf_len, 
                      buf_len-9, 4);
        //char*[4]转16bit数据
        uint16_t len = __get16bitFromChar(data_len);
        
        //按照len长度, 获取对应图像数据
        // 从 1 开始
        // 到 buf_len-9('xxxxLOAD_') 结束
        uint16_t current_pos = 1;  //buf[0]是'/', 要跳过
        //
        //====== 注释旧代码
        // while (current_pos <= len)
        // {
        //     uint16_t img_val = __get8bitFromChar(&buf[current_pos]); //取两个字符(每个字符存4bit数据), 拼接出对应8bit数据
        //  
        //     //将图像数据交给EPD
        //     handler_ePaperSetData((uint8_t)img_val);
        //
        //     current_pos += 2; //buf[]中每个元素存有4bit信息, 每次取2个元素
        // }
        //
        //将图像数据交给EPD
        ePaper_SetImgDataByChar(&buf[current_pos], len, Img_Type_Photo); //buf[0]是'/', 要跳过, 从1开始
        //将图像数据写入SDCard
        SDCard_File_Write_Img(&buf[1], len);

        // ESP_LOGI(TAG_WEB, "step2, len:%d, pos:%d", len, current_pos-2);
    }
    //------ data_cmd='NEXT_'
    else if(strstr(data_cmd, "NEXT"))
    {
        ESP_LOGI(TAG_WEB, "step3");
    }
    //------ data_cmd='SHOW_'
    else if(strstr(data_cmd, "SHOW"))
    {
        //EPD显示
        handler_ePaperDisplay(); //可能超时触发WDT
        //SDCard文件操作
        SDCard_File_Close(); //SDCard关闭文件

        ESP_LOGI(TAG_WEB, "step4");
    }
    //------ 其他情况不处理
    else
    {
        ESP_LOGI(TAG_WEB, "step??? get unknow cmd");
        return ESP_FAIL;
    }

    //------------ step2 response Client
    // ESP_LOGI(TAG_WEB, "response client...");
    const char resp[] = "POST Response!";
    httpd_resp_set_status(req, "200 OK");
    httpd_resp_sendstr(req, resp);

    return ESP_OK;
}

//================== 用于EMBED_FILES, 先以SDCard+FS替代
// void __sendWebFile(httpd_req_t *req, uint8_t type)
// {
//     const size_t file_size = (WebFiles[type].fileEnd - WebFiles[type].fileStart);
        
//     httpd_resp_set_status(req, "200 OK");
//     httpd_resp_set_type(req, WebFiles[type].fileType);
//     httpd_resp_send_chunk(req, (const char *)WebFiles[type].fileStart, file_size);
//     httpd_resp_sendstr_chunk(req, NULL);

//     ESP_LOGI(TAG_WEB, "return file - %s", WebFiles[type].who);
// }

//================== 从SDCard读取Web文件, 并发送
static void __sendWebFileFromFS(httpd_req_t *req, uint8_t type)
{
    char *file_path = WebFilesFS[type].filePath; //获取文件路径

    if(SDCard_File_Open(WebFilesFS[type].filePath, FILE_MODE_READ) == ESP_OK) //检查并打开文件
    {
        ESP_LOGI(TAG_WEB, "open web file: %s", file_path);

        //返回status=OK
        httpd_resp_set_status(req, "200 OK");
        //返回文件类型
        httpd_resp_set_type(req, WebFilesFS[type].fileType); 
        //返回数据
        char file_buf[1024];
        while(SDCard_File_Read(file_buf, 1024) == ESP_OK) //读取文件
        {
            httpd_resp_send_chunk(req, (const char *)file_buf, strlen(file_buf));
            memset(file_buf, NULL, sizeof(file_buf));
        }
        SDCard_File_Close(); //关闭文件
        // ESP_LOGI(TAG_WEB, "CLOSE FILE");
        //返回NULL表示结束
        httpd_resp_sendstr_chunk(req, NULL);
    }
    else
    {
        ESP_LOGI(TAG_WEB, "NO FILE: %s", file_path);
    }
}


//======================================= 获取部分字符
//
// description:
//      从src中拷贝部分字符到dest, 序号以src的start开始, 到start+len结束(也就是[start, start+len))
//
// input:
//    *dest, 目标位置的指针
//    dest_len, 目标的最大长度
//    *src, 源位置的指针
//    src_len, 源的长度
//    start, 待拷贝的起始位置
//    len, 待拷贝的长度
//
static esp_err_t __getSomeChar(char *dest, int dest_len, 
                        char *src, int src_len, 
                        int start, int len)
{
    if((dest_len > len) || (start+len > src_len))
        return ESP_FAIL;
    
    for(int idx = 0; idx < len; idx++)
    {
        // ESP_LOGI(TAG_MAIN, "src:%c", src[start + idx]);
        dest[idx] = src[start + idx];
    }

    return ESP_OK;
}

//用*src的前2个元素拼接出8bit数据
//'a'表示0(0000(2)) ... 'p'表示16(1111(2))
//
// input:
//      src[0]是第一个字符, 为低4bit
//      src[1]是第二个字符, 为高4bit
// output:
//      拼接出的8bit数据
//
static int16_t __get8bitFromChar(char *src)
{
    
    if((src[0] < 'a') || (src[0] > 'p') || //低4bit
       (src[1] < 'a') || (src[1] > 'p')) //高4bit
        return -1;

    return ((int16_t)src[0]-'a') | (((int16_t)src[1]-'a') << 4);
}

//用*src的前4个元素拼接出16bit数据
//
static int16_t __get16bitFromChar(char *src)
{
    //拿到低8bit
    int16_t low_8 = __get8bitFromChar(&src[0]);
    //拿到高8bit
    int16_t high_8 = __get8bitFromChar(&src[2]);

    if((low_8 == -1) || (high_8 == -1))
        return -1;

    return low_8 | (high_8 << 8);
}





